home *** CD-ROM | disk | FTP | other *** search
/ PC World Interactive 7 / PC World Interactive 7.iso / program / asprog.EXE / E.ASM < prev    next >
Assembly Source File  |  1996-07-05  |  65KB  |  2,467 lines

  1. ;E 1.3.  Copyright (C) David Nye, 1990, 1991, all rights reserved.
  2. ;Assemble with TASM.  Link: tlink /t e (makes a .COM file).
  3.  
  4. IDEAL
  5. ;Constants
  6.  
  7. LINEWIDTH       EQU 80          ;Length of string storage for line
  8. SCREENLENGTH    EQU 24          ;Number of rows in display window
  9. MAXLINES        EQU 8096        ;Size of arrays of line pointers
  10. BUFFERLENGTH    EQU 1200h       ;Length of file buffer
  11. STACKSIZE       EQU 50h         ;Length of stack
  12. BELL            EQU 7           ;Some non-printing chars
  13. BS              EQU 8
  14. HT              EQU 9
  15. LF              EQU 10
  16. CR              EQU 13
  17. CRLF            EQU 0A0Dh
  18. CTRL_Z          EQU 26
  19. ESCAPE          EQU 27
  20. DEL             EQU 127
  21. PROGLENGTH = EndOfProgram - Orig + 100h  ;Length of .COM file
  22.  
  23.  
  24. ;Create a named string
  25. MACRO String Name, Text
  26.         LOCAL LL
  27. Name    db LL-Name-1, Text
  28. LL:
  29. ENDM
  30.  
  31. ;Make <Name> a byte ptr to <Length> bytes of storage after end of code.
  32. ;Like a .DATA? segment for .COM file, so uninitialized data doesn't take up
  33. ;room in the .COM file.
  34. MACRO B? Name, Length
  35.         LOCAL where
  36.         where = EndOfProgram + BuffersCount
  37. Name    EQU BYTE PTR where
  38.         BuffersCount = BuffersCount + Length
  39. ENDM
  40.  
  41. ;Like B? but for word storage
  42. MACRO W? Name, Length
  43.         LOCAL where
  44.         where = EndOfProgram + BuffersCount
  45. Name    EQU WORD PTR where
  46.         BuffersCount = BuffersCount + Length + Length
  47. ENDM
  48.  
  49. BuffersCount = 0
  50.  
  51.  
  52. MODEL TINY
  53. CODESEG
  54. ORG 100h
  55. Orig:
  56. jmp Start
  57.  
  58. String copyRight 'E 1.3.  Copyright (C) David Nye, 1991.  All rights reserved.'
  59.  
  60. ;Some defaults, use ECONFIG to change
  61.  
  62. colorAttributes dw 1770h        ;Default status, text color video attributes
  63. tabSize         db 4            ;Tab increment
  64. inserting?      db -1           ;True if in insert mode
  65. autoIndent?     db -1           ;True if in autoindent mode
  66. startInText?    db 0            ;Set to true to start up in text mode
  67. zLMargin        db 8            ;Left margin setting for Alt Z
  68. zRMargin        db LINEWIDTH-8  ;Right margin setting for Alt Z
  69.  
  70.  
  71. ;Strings
  72.  
  73. String cantOpenMsg,     "Can't open file."
  74. String rdErrorMsg,      'Error reading file.'
  75. String fileErrorMsg,    "Can't save file."
  76. String diskFullMsg,     'Disk full.'
  77. String noRoomMsg,       'Out of memory.'
  78. String sysErrMsg,       'System error.'
  79. String notMarkingMsg,   'Not marking.'
  80. String setLabelMsg,     'Label (0-9): '
  81. String setTabsMsg,      'Tab width: '
  82. String newFileMsg,      'File name: '
  83. String gotoMsg          'Jump to what line? '
  84. String editingMsg       <'Help F1',186,'Editing: '>
  85. String findMsg          'Find: '
  86. String replaceMsg       'Replace with: '
  87. String notFoundMsg      'No more matching strings found.'
  88. String anyKeyMsg        'Press any key to continue.'
  89. String ctrlCMsg         '*Break*'
  90. String cancelledMsg     'Cancelled.'
  91. BAK                     db  '.BAK', 0
  92. comspec$                db  'COMSPEC='
  93. helpMsg                 db 'CURSOR left           left arrow, ^S    '
  94.                         db 'BLOCK begin                 @B          '
  95.                         db '  right               right arrow, ^D   '
  96.                         db '  copy block to buffer      @C *        '
  97.                         db '  word left           ^left arrow, ^A   '
  98.                         db '  delete block to buffer    @D *        '
  99.                         db '  word right          ^right arrow, ^F  '
  100.                         db '  insert block from buffer  @I *        '
  101.                         db '  tab right, left     Tab, Shift Tab    '
  102.                         db '  empty block buffer        @E *        '
  103.                         db '  start, end of line  Home, End         '
  104.                         db '  unmark                    @U          '
  105.                         db '  line up             up arrow, ^E      '
  106.                         db 'FIND                        @F +        '
  107.                         db '  line down           down arrow, ^X    '
  108.                         db '  replace                   @R +        '
  109.                         db '  page up             PgUp, ^R          '
  110.                         db '  find/replace all          @A +        '
  111.                         db '  page down           PgDn, ^C          '
  112.                         db 'SAVE and continue           @S          '
  113.                         db '  start of file       ^PgUp             '
  114.                         db '  save and exit             @X          '
  115.                         db '  end of file         ^PgDn             '
  116.                         db '  toggle kill save at exit  @K          '
  117.                         db 'DELETE                Del               '
  118.                         db '  open another file         @O+         '
  119.                         db '  backspace           Backspace         '
  120.                         db 'JUMP to line #              @J          '
  121.                         db '  delete word left    ^[                '
  122.                         db '  set, goto label (0-9)     @L, @G      '
  123.                         db '  delete word right   ^], ^T            '
  124.                         db 'MARGIN set L, R             ^Home, ^End '
  125.                         db '  delete rest of line ^\                '
  126.                         db '  wrap paragraph            @W          '
  127.                         db '  delete line         ^-, ^Y            '
  128.                         db '  set tabs                  @T          '
  129.                         db '  undelete line       ^^                '
  130.                         db '  toggle autoindent         ^@          '
  131.                         db 'INSERT mode toggle    Ins               '
  132.                         db '  toggle text/prog mode     @Z          '
  133.                         db '  insert raw char     @= (+80h w shift) '
  134.                         db 'SHELL to DOS, run EFn.BAT   F2, F3-F6 * '
  135.                         db 80 dup (' ')
  136.                         db '@ = Alt, ^ = Ctrl, * = to/from file if s'
  137.                         db 'hifted, + = use last string if shifted. '
  138.                         db 'Status line flags:  Insert  Overwrite  C'
  139.                         db 'hanged  AutoIndent  [ LMargin  ] RMargin'
  140.  
  141.  
  142. ;EXEC function parameter block
  143.  
  144. EXECParams      dw 0
  145. EXECCmdLineOff  dw 0
  146. EXECCmdLineSeg  dw 0, -1, -1 , -1 , -1
  147. EXECBAT         db 0, '/c EF'
  148. EXECFnumber     db 'x.BAT '
  149. EXECFileName    db 20 dup (0)
  150.  
  151. ;Variables
  152.  
  153. newFile?        db 0            ;True if new file
  154. marking?        db 0            ;True if marking text
  155. changed?        db 0            ;True if original file has been changed
  156. isBAKed?        db 0            ;True if .BAK file already written
  157. needCopies?     db -1           ;True unless lines in buffer were just deleted
  158. autoReplace?    db 0            ;-1 if auto-replace with shift, 1 without shift
  159. noEscape?       db 0            ;True if prompt demands response
  160. labelTable      dw 10 dup (0)   ;Table of line pointers assigned to labels
  161.  
  162. ;These variables and buffers are allocated space following .COM file
  163. W? sstack, STACKSIZE            ;Stack goes here
  164. STACKTOP = EndOfProgram + BuffersCount
  165. B? attribNl, 1                  ;Text and status line attributes
  166. B? attribInv, 1
  167. W? cursorShape, 1               ;Line cursor parameters for color or mono
  168. B? fName?, 1                    ;True if file name given on command line
  169. B? justFound?, 1                ;True if no other commands since last Find
  170. B? swapped?, 1                  ;True if edited file swapped out during EXEC
  171. W? lMargin, 1                   ;Current margins
  172. W? rMargin, 1
  173. W? fHandle, 1                   ;File handle
  174. W? lastLine, 1                  ;Index of last line in file
  175. W? blockPtrsLast, 1             ;Index of last line in block buffer
  176. W? top, 1                       ;Index of first line on screen
  177. W? bottom, 1                    ;Index of last line on screen
  178. W? mark, 1                      ;Start of marking for block command
  179. W? here, 1                      ;Temporaries
  180. W? spTemp, 1
  181. W? comspecPtrOff, 1             ;Pointer to COMSPEC value: offset, segment
  182. W? comspecPtrSeg, 1
  183. W? bufferPtr, 1                 ;Multipurpose buffer pointer
  184. W? hereCol, 1
  185. W? topSegPtr, 1
  186. W? topSeg, 1
  187. W? topIndex, 1
  188. W? videoSegment, 1              ;Segment of system's video memory
  189. W? heapStart, 1                 ;Segment of start of heap
  190. W? heapPtr, 1                   ;Segment pointer to next free paragraph in heap
  191. B? fName, 50                    ;File name in ASCIIZ format
  192. B? otherFName, 50               ;Name of second file
  193. W? otherLine, 1                 ;Line of cursor in second file
  194. ESSENTIALS = BuffersCount       ;Buffers above here not saved with shell + swap
  195. W? temp, 1                      ;Temporary storage
  196. B? fNameBAK, LINEWIDTH          ;Current file with .BAK extension added
  197. B? fNameTemp, LINEWIDTH         ;File name for block read/writes, shell
  198. B? pad, LINEWIDTH               ;Scratch buffer
  199. B? findString, LINEWIDTH        ;Search string for Find command
  200. B? replaceString, LINEWIDTH     ;New string for Replace command
  201. W? linePtrs, MAXLINES           ;List of line pointers
  202. W? blockPtrs, MAXLINES          ;Line pointers for block or line deletes
  203. B? buffer, BUFFERLENGTH         ;File buffer
  204. ENDFBUFFER = BuffersCount
  205.  
  206. ;Jump tables:   ^ = Ctrl, @ = Alt, # = Shift.
  207. ctrlTable       dw na           ;Undefined
  208.                 dw WordLeft     ;^A
  209.                 dw na           ;^B
  210.                 dw PageDown     ;^C
  211.                 dw Right        ;^D
  212.                 dw Up           ;^E
  213.                 dw WordRight    ;^F
  214.                 dw na           ;^G or BEL
  215.                 dw BackSpace    ;^H or BS
  216.                 dw Tab          ;^I or HT
  217.                 dw na           ;^J or LF
  218.                 dw na           ;^K or VT
  219.                 dw na           ;^L or FF
  220.                 dw CRet         ;^M or CR
  221.                 dw na           ;^N or SO
  222.                 dw na           ;^O or SI
  223.                 dw na           ;^P
  224.                 dw na           ;^Q or DC1
  225.                 dw PageUp       ;^R or DC2
  226.                 dw Left         ;^S or DC3
  227.                 dw DeleteWordR  ;^T or DC4
  228.                 dw na           ;^U
  229.                 dw na           ;^V
  230.                 dw na           ;^W
  231.                 dw Down         ;^X or CAN
  232.                 dw DeleteLine   ;^Y
  233.                 dw na           ;^Z
  234.                 dw DeleteWordL  ;^[
  235.                 dw DeleteToEOL  ;^\
  236.                 dw DeleteWordR  ;^]
  237.                 dw UndeleteLine ;^^
  238.                 dw DeleteLine   ;^-
  239.  
  240. auxTable        dw 3 DUP (na)   ;Undefined
  241.                 dw AutoIndent   ;^@ or NUL
  242.                 dw 11 DUP (na)  ;Undefined
  243.                 dw ReverseTab   ;#Tab
  244.                 dw na           ;@Q
  245.                 dw Wrap         ;@W
  246.                 dw EmptyBuffer  ;@E
  247.                 dw Replace      ;@R
  248.                 dw SetTabs      ;@T
  249.                 dw na           ;@Y
  250.                 dw Unmark       ;@U
  251.                 dw InsertBlock  ;@I
  252.                 dw OtherFile    ;@O
  253.                 dw na           ;@P
  254.                 dw 4 DUP (na)   ;Undefined
  255.                 dw ReplaceAll   ;@A
  256.                 dw Save         ;@S
  257.                 dw DeleteBlock  ;@D
  258.                 dw Find         ;@F
  259.                 dw GotoLabel    ;@G
  260.                 dw Help         ;@H
  261.                 dw Jump         ;@J
  262.                 dw Kill         ;@K
  263.                 dw SetLabel     ;@L
  264.                 dw 5 DUP (na)   ;Undefined
  265.                 dw ToggleWPMode ;@Z
  266.                 dw Exit         ;@X
  267.                 dw Copy         ;@C
  268.                 dw na       ;@V
  269.                 dw BeginBlock   ;@B
  270.                 dw na           ;@N
  271.                 dw na           ;@M
  272.                 dw 8 DUP (na)   ;Undefined
  273.                 dw Help         ;F1
  274.                 dw Shell        ;F2
  275.                 dw F3BAT        ;F3
  276.                 dw F4BAT        ;F4
  277.                 dw F5BAT        ;F5
  278.                 dw F6BAT        ;F6
  279.                 dw na           ;F7
  280.                 dw na           ;F8
  281.                 dw na           ;F9
  282.                 dw na           ;F10
  283.                 dw 2 DUP (na)   ;Undefined
  284.                 dw HomeLine     ;Home
  285.                 dw Up           ;Up arrow
  286.                 dw PageUp       ;PgUp
  287.                 dw na           ;Undefined
  288.                 dw Left         ;Left arrow
  289.                 dw na           ;Undefined
  290.                 dw Right        ;Right arrow
  291.                 dw na           ;Undefined
  292.                 dw EndLine      ;End
  293.                 dw Down         ;Down arrow
  294.                 dw PageDown     ;PgDn
  295.                 dw ToggleIns    ;Ins
  296.                 dw Delete       ;Del
  297.                 dw na           ;#F1
  298.                 dw Shell        ;#F2
  299.                 dw F3BAT        ;#F3
  300.                 dw F4BAT        ;#F4
  301.                 dw F5BAT        ;#F5
  302.                 dw F6BAT        ;#F6
  303.                 dw na           ;#F7
  304.                 dw na           ;#F8
  305.                 dw na           ;#F9
  306.                 dw na           ;#F10
  307.                 dw 20 DUP (na)  ;[^Fn, @Fn]
  308.                 dw na           ;^PrtSc
  309.                 dw WordLeft     ;^Left arrow
  310.                 dw WordRight    ;^Right arrow
  311.                 dw SetRMargin   ;^End
  312.                 dw BottomFile   ;^PgDn
  313.                 dw SetLMargin   ;^Home
  314.                 dw 10 DUP (na)  ;[Alt numbers]
  315.                 dw na           ;@-
  316.                 dw InsertRaw    ;@=
  317.                 dw TopFile      ;^PgUp
  318.  
  319. ;******************************************************************************
  320.  
  321. Start:
  322.   mov ax, cs
  323.   mov ds, ax
  324.   mov es, ax
  325.   mov [EXECCmdLineSeg], ax      ;Store current segment for EXEC function
  326.   add ax, ((PROGLENGTH + ENDFBUFFER) SHR 4) + 1
  327.   mov [heapStart], ax           ;Compute start of free memory in paragraphs
  328.   mov sp, OFFSET STACKTOP
  329.   mov si, 80h                   ;Make pointer to command tail
  330.   mov cl, [si]                  ;Get filename length
  331.   sub ch, ch
  332.   mov [fName?], cl              ;Save a copy
  333.   mov al, ' '                   ;Skip leading blanks
  334. @@L1:
  335.   inc si
  336.   cmp al, [si]
  337.   loope @@L1
  338.   inc cx
  339.   mov di, OFFSET fName          ;Move command tail to FName
  340.   rep movsb
  341.   sub al, al                    ;Make ASCIIZ string
  342.   stosb
  343.   mov ax, 2523h                 ;Redirect Ctrl C handler
  344.   mov dx, OFFSET Cancel
  345.   int 21h
  346.   mov [byte OtherFName], 0      ;Prevent Alt Shift O crash without prev. file
  347.   mov ah, 0Fh                   ;Set defaults for color or mono adapter
  348.   int 10h
  349.   mov bx, 0B000h                ;If mode 7 (= MDA or Herc), video seg=B000h,
  350.   mov cx, 0770h                 ;Use nl, inv video for text, status line
  351.   mov dx, 0C0Dh                 ;Set cursor shapes for mono
  352.   cmp al, 7
  353.   je @@L2
  354.   mov bx, 0B800h                ;Otherwise video seg=B800h,
  355.   mov cx, [colorAttributes]     ;Use default color attributes,
  356.   mov dx, 0607h                 ;Color cursor size
  357. @@L2:
  358.   xor ax, ax                    ;Code to allow E to run under DESQview,
  359.   mov es, ax                    ; contributed by Mike Robertson (bix:seamus)
  360.   mov ah, 0FEh
  361.   int 10
  362.   or al, al
  363.   jne @@L3a
  364.   mov ax, es
  365.   or ax, ax
  366.   je @@L3a
  367.   mov bx, es
  368. @@L3a:
  369.   mov [videoSegment], bx
  370.   mov [attribNl], ch
  371.   mov [attribInv], cl
  372.   mov [cursorShape], dx
  373.   mov es, [2Ch]                 ;Find COMSPEC
  374.   sub di, di
  375. @@L3:
  376.   mov si, OFFSET comspec$
  377.   mov cx, 8
  378.   repe cmpsb
  379.   jne @@L3
  380.   mov [comspecPtrOff], di
  381.   mov [comspecPtrSeg], es
  382.  
  383. InitFile:
  384.   mov dx, OFFSET fName          ;Open file and set up list of line pointers
  385.   cmp [fName?], 0               ;If no file name specified on command line,
  386.   jne @@L0
  387.   mov [noEscape?], -1           ;Prompt for it
  388.   call GetFileName
  389. @@L0:
  390.   call OpenFile
  391.   mov [lMargin], 0              ;Set initial margins
  392.   mov [rMargin], LINEWIDTH - 1
  393.   cmp [startInText?], 0
  394.   je NextKey
  395.   call ToggleWPMode
  396.  
  397. NextKey:
  398.   call Redraw                   ;Redraw screen, status line
  399. NextNoRedraw:
  400.   call DrawCursor               ;Place cursor
  401.   sub ah, ah                    ;Get keypress to AL
  402.   int 16h
  403.   or al, al                     ;Check for control codes
  404.   je IsAux
  405.   cmp al, ' '
  406.   jb IsCtrl
  407.   call Insert                   ;Insert or overwrite if none
  408.   jmp NextKey
  409.  
  410. IsAux:
  411.   xchg al, ah                   ;Get aux code
  412.   cmp al, 132
  413.   ja NextKey
  414.   mov si, OFFSET auxTable       ;Jump indirect to appropriate routine
  415. DoTableJump:
  416.   shl ax, 1
  417.   add si, ax
  418.   call [WORD si]
  419.   jmp NextKey
  420.  
  421. IsCtrl:
  422.   mov si, OFFSET ctrlTable      ;Jump to routine through table
  423.   sub ah, ah
  424.   jmp DoTableJump
  425.  
  426. InsertRaw:
  427.   call Shifted?                 ;Set flag if shifted
  428.   mov dl, al
  429.   sub ah, ah                    ;Get keypress to AL
  430.   int 16h
  431.   or al, al                     ;Check for aux code, ignore InsertRaw if found
  432.   je IsAux
  433.   cmp dl, 0                     ;If shift was down set high bit
  434.   jz @@L1
  435.   or al, 80h
  436. @@L1:
  437.   call Insert                   ;Insert
  438.   jmp NextKey
  439.  
  440. SwapNames:
  441. ;Swap old and new file names prior to opening new file
  442.   mov si, OFFSET fName
  443.   mov di, OFFSET otherFName
  444.   mov cx, 50
  445. @@L1:
  446.   mov al, [si]
  447.   xchg al, [di]
  448.   mov [si], al
  449.   inc si
  450.   inc di
  451.   loop @@L1
  452.   ret
  453.  
  454. OtherFile:
  455. ;Open another file
  456.   call Shifted?                 ;Set flag if shifted
  457.   mov dh, [byte OtherFName]     ;Ignore it if no prior file
  458.   or dh, dh
  459.   jz @@L1
  460.   mov dh, al
  461. @@L1:
  462.   call Save                     ;Save current file if altered
  463.   mov ax, [blockPtrsLast]       ;If block buffer is empty,
  464.   sub ax, OFFSET blockPtrs
  465.   jne @@L2
  466.   mov ax, [heapStart]           ; reset heap pointer to start
  467.   mov [heapPtr], ax
  468.   jmp SHORT GetOther            ; prompt for new file name
  469. @@L2:
  470.   shr ax, 1                     ;Else move lines with pointers in block buffer
  471.   mov cx, ax                    ; to start of heap (load new file above them)
  472.   mov dl, 5
  473.   mul dl
  474.   add ax, [heapStart]           ;Calculate upper limit of target zone
  475.   mov [heapPtr], ax             ; which will also be new value of heap pointer
  476.   mov bx, OFFSET blockPtrs      ;For each pointer in block buffer,
  477. @@L3:
  478.   cmp [bx], ax                  ;If its line is already within target zone,
  479.   jae @@L4
  480.   mov es, [bx]                  ;Set high bit of first char in line to mark it
  481.   or [byte es:0], 80h           ; (we won't need to move these lines)
  482. @@L4:
  483.   inc bx                        ;Next pointer
  484.   inc bx
  485.   loop @@L3
  486.   push ds
  487.   mov bx, OFFSET blockPtrs      ;For each pointer in block buffer:
  488.   mov es, [heapStart]
  489. @@L4a:
  490.   test [byte es:0], 80h         ;If high bit set in target line,
  491.   jne @@L7                      ; line already in use, try next target line
  492.   mov ds, [cs:bx]               ;If high bit set in source line,
  493.   test [byte 0], 80h
  494.   je @@L5                       ; don't need to move it (already there)
  495.   inc bx                        ; Next source line, same target line
  496.   inc bx
  497.   jmp SHORT @@L8
  498. @@L5:
  499.   mov cx, 40                    ;Else move one line
  500.   sub si, si
  501.   sub di, di
  502.   rep movsw
  503.   mov [cs:bx], es               ;Update pointer
  504. @@L6:
  505.   inc bx                        ;Next block pointer
  506.   inc bx
  507. @@L7:
  508.   mov ax, es                    ;Next target line
  509.   add ax, 5
  510.   mov es, ax
  511. @@L8:
  512.   segcs                         ;Loop until all lines moved
  513.   cmp bx, [blockPtrsLast]
  514.   jb @@L4a
  515.   pop ds
  516.   mov bx, OFFSET blockPtrs      ;Reset high bits of all first chars set high
  517. @@L9:
  518.   mov es, [bx]
  519.   and [byte es:0], 7Fh
  520.   inc bx
  521.   inc bx
  522.   cmp bx, [blockPtrsLast]
  523.   jb @@L9
  524. GetOther:
  525.   call SwapNames
  526.   cmp dh, 0                     ;If Shifted, use last file
  527.   je @@L1
  528.   mov dx, OFFSET fName
  529.   call OpenFile1
  530.   mov bx, [otherLine]           ;Restore cursor to previous position
  531.   mov ax, [here]
  532.   mov [otherLine], ax
  533.   jmp NewLine
  534. @@L1:
  535.   mov [noEscape?], -1
  536.   mov ax, [here]                ;Save current file name and cursor line
  537.   mov [otherLine], ax
  538.   mov dx, OFFSET fName          ;Prompt for new file name
  539.   call GetFileName
  540.   jmp SHORT OpenFile1           ;Read in new file above block buffer lines
  541.  
  542. OpenFile:
  543. ;Open file, load if found, then close.  Call with dx -> ASCIIZ file name.
  544.   mov ax, OFFSET blockPtrs      ;Reset block buffer pointer
  545.   mov [blockPtrsLast], ax
  546.   mov ax, [heapStart]           ;Begin loading at start of heap
  547.   mov [heapPtr], ax
  548. OpenFile1:
  549.   mov [newFile?], 0
  550.   mov [changed?], 0
  551.   mov ax, 3D00h                 ;Try to open file
  552.   int 21h
  553.   jnc OldFile
  554.   mov [newFile?], -1            ;If no such file, remind me to create a new one
  555. OpenNewFile:
  556.   call NewFile
  557.   jmp SHORT XOpenFile
  558. OldFile:
  559.   mov [fHandle], ax             ;Else save file handle
  560.   mov bx, OFFSET linePtrs       ;Read file in
  561.   mov dx, ax
  562.   call ReadFile
  563.   dec bx
  564.   dec bx
  565.   mov [lastLine], bx            ;Save index of last line
  566.   mov bx, [fHandle]             ;Close file
  567.   mov ah, 3Eh
  568.   int 21
  569. XOpenFile:
  570.   mov bx, OFFSET linePtrs       ;Reset row, screen pointers
  571.   mov [top], bx
  572.   mov es, [bx]
  573.   sub di, di
  574.   ret
  575.  
  576. GetFileName:
  577. ;Prompt for file name.  Abort if null name.  Call with buffer address in DX.
  578.   push si
  579.   push dx
  580.   mov si, OFFSET newFileMsg     ;Print prompt
  581.   call Prompt
  582.   pop dx
  583.   call GetString                ;Get file name
  584.   mov si, dx                    ;Convert to ASCIIZ
  585.   add si, ax
  586.   mov [BYTE si], 0
  587.   pop si
  588.   ret
  589.  
  590. GetString:
  591. ;Get string to [DX], return count (minus CR/LF) in AX.  Abort if null string.
  592.   push bx
  593.   push cx
  594.   push si
  595.   push di
  596.   push es
  597.   push dx
  598. @@L2:
  599.   mov dx, OFFSET pad            ;Get string
  600.   mov ah, 3Fh
  601.   sub bx, bx
  602.   mov cx, 20
  603.   int 21h
  604.   dec ax                        ;Strip CR/LF
  605.   dec ax
  606.   jnz @@L1                      ;Abort if null string
  607.   cmp [noEscape?], 0            ; unless escape precluded
  608.   je @@L0
  609.   call Beep
  610.   mov si, OFFSET newFileMsg
  611.   call Prompt
  612.   jmp @@L2
  613. @@L0:
  614.   mov si, OFFSET cancelledMsg
  615.   jmp Abort
  616. @@L1:
  617.   mov cx, ax                    ;Copy temporary copy of string to [DX]
  618.   pop dx
  619.   push ax
  620.   mov ax, ds
  621.   mov es, ax
  622.   mov si, OFFSET pad
  623.   mov di, dx
  624.   rep movsb
  625.   pop ax
  626.   pop es
  627.   pop di
  628.   pop si
  629.   pop cx
  630.   pop bx
  631.   mov [noEscape?], 0
  632.   ret
  633.  
  634. ReadFile:
  635. ;Load file with handle in DX, assigning segment pointers starting at BX
  636.   push es
  637.   mov ax, [heapPtr]
  638.   mov es, ax
  639.   sub cx, cx
  640.   sub di, di
  641. FillBuffer:
  642.   push bx                       ; Fill buffer
  643.   push cx
  644.   push dx
  645.   mov bx, dx
  646.   mov ah, 3Fh
  647.   mov cx, BUFFERLENGTH
  648.   mov dx, OFFSET buffer
  649.   int 21h
  650.   jnc @@L1                      ; Check for read error
  651.   jmp ReadError
  652. @@L1:
  653.   pop dx
  654.   pop cx
  655.   pop bx
  656.   mov si, OFFSET buffer         ; Set pointers
  657.   add ax, si
  658.   mov [bufferPtr], ax
  659.   cmp ax, OFFSET buffer         ;Exit if empty buffer
  660.   je EndOfFile
  661.   cmp [byte si], LF             ;Skip LF if first char in buffer
  662.   jne SHORT NextLine
  663.   inc si
  664. NextLine:
  665.   mov al, [si]                  ;Get next char
  666.   cmp al, CR                    ;If char is CR, end of line
  667.   jne @@L2
  668.   inc si                        ; move past CR
  669.   cmp [byte si], LF             ; and LF if present
  670.   jne @@L1
  671.   inc si
  672. @@L1:
  673.   call EndOfLine                ; pad out line with spaces and save it
  674.   jmp SHORT @@L3
  675. @@L2:
  676.   cmp al, HT                    ;Else if a tab, expand it
  677.   jne @@L2a
  678.   push cx
  679.   mov al, ' '
  680.   mov cl, [tabSize]
  681.   sub ch, ch
  682.   rep stosb
  683.   pop cx
  684.   sub cl, [tabSize]
  685.   sbb ch, 0
  686.   inc si
  687.   jmp SHORT @@L3
  688. @@L2a:
  689.   movsb                         ;Else add char to line
  690.   dec cx
  691. @@L3:
  692.   cmp si, [bufferPtr]           ;Loop until end of buffer
  693.   jb NextLine
  694.   cmp si, OFFSET ENDFBUFFER     ;If buffer less than full, indicates end of file
  695.   jae FillBuffer
  696. EndOfFile:
  697.   call EndOfLine                ;Finish up present line
  698.   mov [heapPtr], es             ;Update pointer to start of free heap space
  699.   pop es
  700.   ret
  701.  
  702. EndOfLine:
  703.   add cx, LINEWIDTH             ;Pad to end with spaces
  704.   jle @@L1                      ;Truncate lines longer than LINEWIDTH chars
  705.   mov al, ' '
  706.   rep stosb
  707. @@L1:
  708.   mov [bx], es                  ;Store segment of this line
  709.   mov ax, es                    ;Next line
  710.   add ax, 5
  711.   cmp ax, 0A000h                ;Out of room?
  712.   jb SHORT @@L2
  713.   jmp NoRoom
  714. @@L2:
  715.   mov es, ax
  716.   inc bx
  717.   inc bx
  718.   sub di, di
  719.   sub cx, cx
  720. Ret3:
  721.   ret
  722.  
  723. Redraw:
  724. ;Redraw screen and status line
  725.   mov [here], bx
  726.   mov [hereCol], di
  727.   push bx
  728.   push di
  729.   push ds
  730.   push es
  731.   push di
  732.   mov es, [videoSegment]        ;Get segment for display
  733.   mov si, OFFSET editingMsg     ;Refresh status line:  "Editing ..."
  734.   call Prompt
  735.   mov di, 34                    ;Tab to column 17
  736.   mov si, OFFSET fName          ; <file name>
  737.   mov ah, [attribInv]
  738. @@S1:
  739.   lodsb
  740.   or al, al
  741.   je @@S2
  742.   stosw
  743.   jmp @@S1
  744. @@S2:
  745.   add di, 6                     ;3 spaces
  746.   mov al, 'L'                   ;"L" <line #>
  747.   stosw
  748.   inc di
  749.   inc di
  750.   mov ax, bx
  751.   sub ax, OFFSET linePtrs
  752.   shr ax, 1
  753.   inc ax
  754.   call PrintInt
  755.   add di, 4                     ;2 spaces
  756.   mov al, 'C'                   ;"C" <column number>
  757.   mov ah, [attribInv]
  758.   stosw
  759.   inc di
  760.   inc di
  761.   pop ax                        ;Get copy of DI as cursor row
  762.   inc ax
  763.   call PrintInt
  764.   mov di, LINEWIDTH * 2 - 12    ;Tab to start of status chars display
  765.   mov al, 'I'                   ;Insert/Overwrite status
  766.   mov ah, [attribInv]
  767.   test [inserting?], -1
  768.   jne @@S3
  769.   mov al, 'O'
  770. @@S3:
  771.   stosw
  772.   mov al, 'C'                   ;Changed status
  773.   test [changed?], -1
  774.   jne @@S5
  775.   mov al, ' '
  776. @@S5:
  777.   stosw
  778.   mov al, 'A'                   ;Autoinsert status
  779.   test [autoIndent?], -1
  780.   jne @@S6
  781.   mov al, ' '
  782. @@S6:
  783.   stosw
  784.   mov al, ' '                   ;L margin set?
  785.   cmp [lMargin], 0
  786.   je @@S7
  787.   mov al, '['
  788. @@S7:
  789.   stosw
  790.   mov al, ' '                   ;R margin set?
  791.   cmp [rMargin], LINEWIDTH - 1
  792.   je @@S8
  793.   mov al, ']'
  794.   stosw
  795. @@S8:
  796.   segcs
  797.   mov al, [attribNl]            ;Mark margins as non-inv chars in status line
  798.   mov di, [lMargin]
  799.   add di, di
  800.   je @@S8a
  801.   inc di
  802.   stosb
  803. @@S8a:
  804.   mov di, [rMargin]
  805.   cmp di, LINEWIDTH - 1
  806.   je @@S8b
  807.   add di, di
  808.   inc di
  809.   stosb
  810. @@S8b:
  811.   mov di, LINEWIDTH * 2         ;Move to next display line
  812.   mov ax, [top]                 ;Compute bottom of screen
  813.   mov bx, ax
  814.   add ax, (SCREENLENGTH - 1) * 2
  815.   cmp ax, [lastLine]            ;If at end of file,
  816.   jle @@L0
  817.   mov ax, [lastLine]            ; stop at lastLine
  818. @@L0:
  819.   mov [bottom], ax
  820. @@L1:                           ;For each row
  821.   mov cx, LINEWIDTH             ;Count of chars per row
  822.   mov ds, [cs:bx]               ;Get pointer to screen line
  823.   sub si, si                    ;Initialize column counter
  824.   segcs
  825.   mov ah, [attribNl]            ;Attribute = inverse video if Marked
  826.   test [cs:marking?], -1
  827.   je @@L2
  828.   segcs
  829.   cmp bx, [mark]
  830.   je @@L1b
  831.   jb @@L1a
  832.   segcs
  833.   cmp bx, [here]
  834.   jbe @@L1b
  835.   jmp SHORT @@L2
  836. @@L1a:
  837.   segcs
  838.   cmp bx, [here]
  839.   jb @@L2
  840. @@L1b:
  841.   segcs
  842.   mov ah, [attribInv]
  843. @@L2:                           ;For each char, write char and attribute
  844.   lodsb
  845.   stosw
  846.   loop @@L2                     ;Next char
  847.   inc bx                        ;Next row
  848.   inc bx
  849.   segcs
  850.   cmp bx, [bottom]              ;Stop if screen full
  851.   jle @@L1
  852.   mov cx, LINEWIDTH * 2 * (SCREENLENGTH + 1)  ;Fill out screen with blanks
  853.   sub cx, di
  854.   shr cx, 1
  855.   segcs
  856.   mov ah, [attribNl]
  857.   mov al, ''
  858.   rep stosw
  859. @@L3:
  860.   pop es
  861.   pop ds
  862.   pop di
  863.   pop bx
  864.   ret
  865.  
  866. DrawCursor:
  867. ;Set cursor shape and place it on screen
  868.   push bx
  869.   mov cx, [cursorShape]         ;Set cursor shape: line for insert mode,
  870.   test [Inserting?], -1
  871.   jne @@L1
  872.   sub ch, ch                    ;Block for overwrite
  873. @@L1:
  874.   mov ah, 1
  875.   int 10h
  876.   sub bx, [top]                 ;Show cursor at current row, column
  877.   shr bx, 1
  878.   inc bx
  879.   mov dh, bl
  880.   mov ax, di
  881.   mov dl, al
  882.   mov ah, 2
  883.   mov bh, 0
  884.   int 10h
  885.   pop bx
  886.   ret
  887.  
  888. Print0:
  889.   call ClearStatus              ;Blank status line
  890.   sub di, di                    ;Starting at beginning of line ...
  891.  
  892. Print:
  893. ;Print string pointed to by SI on status line in inverse video, starting at DI
  894.   push es
  895.   mov es, [videoSegment]
  896.   lodsb                         ;Get count of string to be printed
  897.   mov cl, al
  898.   sub ch, ch
  899.   mov ah, [attribInv]            ;Attribute = inverse video
  900. @@L1:
  901.   lodsb
  902.   stosw
  903.   loop @@L1
  904.   pop es
  905.   ret
  906.  
  907. ClearStatus:
  908. ;Inverse-blank status line
  909.   push di
  910.   push es
  911.   mov es, [videoSegment]
  912.   mov ah, [attribInv]
  913.   mov al, ' '
  914.   mov cx, 80
  915.   sub di, di
  916.   rep stosw
  917.   pop es
  918.   pop di
  919.   ret
  920.  
  921. Cancel:
  922. ;Ctrl C routine
  923.   mov si, OFFSET ctrlCMsg       ;Abort with message ...
  924.  
  925. Abort:
  926. ;Print counted string pointed to by SI on status line and abort
  927.   call Print0                   ;Print error message ...
  928.  
  929. na:
  930. ;Unassigned key or other error.  Beep and abort.
  931.   call Beep                     ;Beep
  932.   mov sp, OFFSET STACKTOP       ;Reset stack pointer to top
  933.   mov bx, [here]                ;Retrieve cursor position in case it was trashed
  934.   mov di, [hereCol]
  935.   call DrawCursor
  936.   jmp NextNoRedraw              ;Restart main editing loop
  937.  
  938. Beep:
  939.   mov ah, 2                     ;Output a BELL
  940.   mov dl, BELL
  941.   int 21h
  942.   ret
  943.  
  944. Prompt:
  945.   push di
  946.   call Print0                   ;Print string at start of line
  947.   mov dx, di                    ;Set cursor to end of printed string ...
  948.   shr dl, 1
  949.   sub dh, dh
  950.   pop di
  951.  
  952. GotoXY:
  953. ;Position cursor at row, column given by DL, DH
  954.   push bx
  955.   mov ah, 2
  956.   sub bx, bx
  957.   int 10h
  958.   pop bx
  959.   ret
  960.  
  961. PrintInt:
  962. ;Print to ES:DI in inverse video the unsigned decimal integer in AX
  963.   sub dx, dx                    ;Start stack with a null
  964.   push dx
  965.   mov cx, 10                    ;Get remainders of successive divisions by 10
  966. @@L1:
  967.   div cx
  968.   add dl, '0'                   ;Convert to ASCII
  969.   mov dh, [attribInv]           ;Attribute is reverse video
  970.   push dx
  971.   sub dx, dx
  972.   or ax, ax
  973.   jne @@L1
  974.   pop ax                        ;Pop and print remainders in reverse order
  975. @@L2:
  976.   stosw
  977.   pop ax
  978.   or ax, ax
  979.   jne @@L2
  980.   ret
  981.  
  982. NewLineC:
  983. ;Jump here if file is changed by a command
  984.   mov [changed?], -1
  985. NewLine:
  986.   mov [justFound?], 0
  987. NewLine0:
  988.   cmp bx, OFFSET linePtrs       ;Check bounds, adjust if necessary
  989.   jge @@L1
  990.   mov bx, OFFSET linePtrs
  991. @@L1:
  992.   cmp bx, [lastLine]
  993.   jle @@L2
  994.   mov bx, [lastLine]
  995. @@L2:
  996.   mov ax, [top]
  997.   cmp bx, ax
  998.   jge @@L3
  999.   mov [top], bx
  1000. @@L3:
  1001.   add ax, (SCREENLENGTH-1)*2
  1002.   cmp bx, ax
  1003.   jle @@L4
  1004.   mov ax, bx
  1005.   sub ax, (SCREENLENGTH-1)*2
  1006.   mov [top], ax
  1007. @@L4:
  1008.   mov es, [bx]                  ;Adjust ES to point to new line
  1009.   ret
  1010.  
  1011. Left:
  1012.   or di, di                     ;If at start of line,
  1013.   jne @@L1
  1014.   call Up                       ; move to end of line above
  1015.   jmp EndLine
  1016. @@L1:
  1017.   dec di                        ; else just decrement cursor
  1018. CursorMoved:
  1019.   mov [justFound?], 0
  1020.   ret
  1021.  
  1022. Right:
  1023.   cmp di, LINEWIDTH - 1         ;If at end of line,
  1024.   jne @@L1
  1025.   sub di, di                    ; move to start of line below
  1026.   jmp Down
  1027. @@L1:
  1028.   inc di                        ; else just increment cursor
  1029.   jmp SHORT CursorMoved
  1030.  
  1031. LScanE:
  1032. ;Scan left past first non-space or start of line
  1033.   mov al, ' '
  1034.   mov cx, di
  1035.   inc cx
  1036.   std
  1037.   repe scasb
  1038.   cld
  1039.   ret
  1040.  
  1041. LScanNE:
  1042. ;Scan left past first space or start of line
  1043.   mov al, ' '
  1044.   mov cx, di
  1045.   inc cx
  1046.   std
  1047.   repne scasb
  1048.   cld
  1049.   ret
  1050.  
  1051. RScanE:
  1052. ;Scan right past first non-space or end of line
  1053.   mov al, ' '
  1054.   mov cx, LINEWIDTH
  1055.   sub cx, di
  1056.   repe scasb
  1057.   ret
  1058.  
  1059. RScanNE:
  1060. ;Scan right past first space or end of line
  1061.   mov al, ' '
  1062.   mov cx, LINEWIDTH
  1063.   sub cx, di
  1064.   repne scasb
  1065.   ret
  1066.  
  1067. WordLeft:
  1068. ;Move left one word
  1069.   or di, di                     ;Do nothing if at start of line
  1070.   je @@Lx
  1071.   mov [justFound?], 0
  1072.   dec di                        ;Else starting at char to left,
  1073.   call LScanE                   ; skip spaces until non-space
  1074.   inc di
  1075.   je @@Lx                       ; or start of line,
  1076.   call LScanNE                  ; then scan to next space or start of line
  1077.   jne @@L1
  1078.   inc di
  1079. @@L1:
  1080.   inc di
  1081. @@Lx:
  1082.   ret
  1083.  
  1084. WordRight:
  1085. ;Move right one word
  1086.   cmp di, LINEWIDTH - 1         ;Do nothing if at end of line
  1087.   je @@Lx
  1088.   mov [justFound?], 0
  1089.   call RScanNE                  ;Skip non-spaces until space
  1090.   jne @@L1                      ; or end of line,
  1091.   dec di
  1092.   call RScanE                   ; then scan to next non-space or end of line
  1093. @@L1:
  1094.   dec di
  1095. @@Lx:
  1096.   ret
  1097.  
  1098. HomeLine:
  1099. ;Move cursor to L margin
  1100.   mov di, [lMargin]
  1101.   mov [justFound?], 0
  1102.   ret
  1103.  
  1104. EndLine:
  1105. ;Move cursor to R margin or end of text on line (but not left of L margin)
  1106.   push cx
  1107.   mov [justFound?], 0
  1108.   mov di, [rMargin]             ;Start at R margin
  1109.   cmp [BYTE es:di], ' '         ;If non-blank, stop here
  1110.   jne @@L2
  1111.   call LScanE                   ;Skip all spaces until non-space
  1112.   je @@L1                       ; or beginning of line
  1113.   inc di
  1114. @@L1:
  1115.   inc di
  1116. @@L2:
  1117.   cmp di, [lMargin]             ;If left of L margin, set to L margin
  1118.   jae @@L3
  1119.   mov di, [lMargin]
  1120. @@L3:
  1121.   pop cx
  1122. Ret2:
  1123.   ret
  1124.  
  1125. Up:
  1126. ;Move cursor up one line
  1127.   cmp bx, OFFSET linePtrs       ;If at top of file already, do nothing
  1128.   je Ret2
  1129.   dec bx
  1130.   dec bx
  1131.   jmp NewLine
  1132.  
  1133. Down:
  1134. ;Move cursor down one line
  1135.   cmp bx, [lastLine]            ;If at last line already, do nothing
  1136.   je Ret2
  1137.   inc bx
  1138.   inc bx
  1139.   jmp NewLine
  1140.  
  1141. PageUp:
  1142. ;Move cursor up one page
  1143.   sub bx, (SCREENLENGTH-1)*2
  1144.   jmp NewLine
  1145.  
  1146. PageDown:
  1147. ;Move cursor down one page
  1148.   add bx, (SCREENLENGTH-1)*2
  1149.   jmp NewLine
  1150.  
  1151. TopFile:
  1152. ;Move cursor to top of file
  1153.   mov bx, OFFSET linePtrs
  1154.   mov [top], bx
  1155.   call HomeLine
  1156.   jmp NewLine
  1157.  
  1158. BottomFile:
  1159. ;Move cursor to bottom of file
  1160.   mov bx, [lastLine]
  1161.   mov es, [bx]
  1162.   mov ax, bx
  1163.   sub ax, (SCREENLENGTH-1)*2
  1164.   cmp ax, OFFSET linePtrs
  1165.   ja @@L1
  1166.   mov ax, OFFSET linePtrs
  1167. @@L1:
  1168.   mov [top], ax
  1169.   call EndLine
  1170.   jmp NewLine
  1171.  
  1172. Tab:
  1173. ;Tab right
  1174.   mov [justFound?], 0
  1175.   mov ax, di                    ;Advance di to next tab stop
  1176.   mov cl, [tabSize]
  1177.   div cl
  1178.   sub cl, ah
  1179.   sub ch, ch
  1180.   add di, cx
  1181.   cmp di, LINEWIDTH             ;If past end of line,
  1182.   jl @@L1
  1183.   mov di, LINEWIDTH - 1         ;Set cursor at end of line
  1184. @@L1:
  1185.   ret
  1186.  
  1187. ReverseTab:
  1188. ;Tab left
  1189.   mov [justFound?], 0
  1190.   mov ax, di                    ;Decrement di to nearest tab stop
  1191.   dec al
  1192.   div [tabSize]
  1193.   mov al, ah
  1194.   sub ah, ah
  1195.   inc al
  1196.   sub di, ax
  1197.   jnc @@L1                      ;Set to start of line if past start
  1198.   sub di, di
  1199. @@L1:
  1200.   ret
  1201.  
  1202. SetLMargin:
  1203. ;Toggle left margin between 0 and present cursor setting
  1204.   cmp [lMargin], 0
  1205.   je @@L1
  1206.   mov [lMargin], 0
  1207.   ret
  1208. @@L1:
  1209.   mov [lMargin], di
  1210.   ret
  1211.  
  1212. SetRMargin:
  1213. ;Toggle right margin between LINEWIDTH-1 and present cursor setting
  1214.   cmp [rMargin], LINEWIDTH-1
  1215.   je @@L1
  1216.   mov [rMargin], LINEWIDTH-1
  1217.   ret
  1218. @@L1:
  1219.   mov [rMargin], di
  1220.   ret
  1221.  
  1222. ToggleWPMode:
  1223. ;Toggle word processing and programming defaults
  1224.   cmp [lMargin], 0
  1225.   jne @@L1
  1226.   cmp [rMargin], LINEWIDTH-1
  1227.   jne @@L1
  1228.   sub ah, ah
  1229.   mov al, [zLMargin]
  1230.   mov [lMargin], ax
  1231.   mov al, [zRMargin]
  1232.   mov [rMargin], ax
  1233.   mov [autoIndent?], 0
  1234.   jmp HomeLine
  1235. @@L1:
  1236.   mov [lMargin], 0
  1237.   mov [rMargin], LINEWIDTH-1
  1238.   mov [autoIndent?], -1
  1239.   jmp HomeLine
  1240.  
  1241. CRet:
  1242. ;Split line at cursor
  1243.   push ds
  1244.   push es
  1245.   push di
  1246.   push es
  1247.   call AddBlankLine             ;Start a new line below current one, ->ES:DI
  1248.   pop ds                        ;DS:SI := current cursor position
  1249.   pop si
  1250.   push di
  1251.   mov cx, LINEWIDTH             ;CX := # chars left on line
  1252.   sub cx, si
  1253.   je @@L2
  1254. @@L1:
  1255.   movsb                         ;Split line,
  1256.   mov [byte si - 1], ' '        ; blank original to end from cursor
  1257.   loop @@L1
  1258. @@L2:
  1259.   pop di
  1260.   pop es
  1261.   pop ds
  1262.   jmp NewLineC
  1263.  
  1264. AddBlankLine:
  1265. ;Insert a new blank line below current one
  1266.   mov cx, 1                     ;Make room for new entry in linePtr
  1267.   call OpenRow
  1268.   inc bx
  1269.   inc bx
  1270.   mov ax, [heapPtr]
  1271.   jmp SHORT BlankLine
  1272.  
  1273. NewFile:
  1274. ;Set up initial blank line of a new file
  1275.   mov ax, [heapStart]           ;Set ES and [bx] to available heap
  1276.   mov bx, OFFSET linePtrs
  1277.   mov [lastLine], bx
  1278. BlankLine:
  1279.   mov [bx], ax
  1280.   mov es, ax
  1281.   add ax, 5
  1282.   mov [heapPtr], ax             ;Update heap pointer (segment value only)
  1283. BlankThisLine:
  1284.   sub di, di                    ;Blank new line
  1285.   mov cx, LINEWIDTH
  1286.   mov al, ' '
  1287.   rep stosb
  1288.   mov di, [lMargin]             ;Home cursor on new line
  1289.   test [autoIndent?], -1        ; or if in autoindent mode,
  1290.   je @@Lx
  1291.   cmp bx, OFFSET linePtrs       ; and this is not first line in file,
  1292.   je @@Lx
  1293.   mov es, [bx - 2]              ; line up with first char of line above
  1294.   call RScanE
  1295.   mov es, [bx]
  1296.   je @@L1                       ; unless above line is blank
  1297.   dec di
  1298.   cmp di, [lMargin]             ; or first char is left of L margin
  1299.   jae @@Lx
  1300. @@L1:
  1301.   mov di, [lMargin]             ; in which case use L margin
  1302. @@Lx:
  1303.   ret
  1304.  
  1305. OpenRow:
  1306. ;Open CX lines at BX in linePtrs
  1307.   push cx
  1308.   push di
  1309.   push es
  1310.   mov ax, ds                    ;DS, ES -> data segment (for linePtr)
  1311.   mov es, ax
  1312.   mov si, [lastLine]            ;SI points to last line's segment pointer
  1313.   mov di, si                    ;DI points CX lines beyond that
  1314.   add di, cx
  1315.   add di, cx
  1316.   mov [lastLine], di            ;Point lastLine to new last line
  1317.   mov cx, si                    ;Count = # lines from here to end
  1318.   sub cx, bx
  1319.   shr cx, 1
  1320.   inc cx
  1321.   std
  1322.   rep movsw                     ;Move array elements up
  1323.   cld
  1324.   pop es
  1325.   pop di
  1326.   pop cx
  1327.   ret
  1328.  
  1329. BackSpace:
  1330. ;Delete char to left of cursor
  1331.   mov ax, di                    ;Unless at first character of file,
  1332.   add ax, bx
  1333.   sub ax, OFFSET linePtrs
  1334.   jz Ret1                       ; do Left then Delete
  1335.   mov [justFound?], 0
  1336.   push di
  1337.   call Left
  1338.   pop ax                        ;Don't do Join if already at end of line ...
  1339.   or ax, ax
  1340.   jne Delete0
  1341.  
  1342. Delete:
  1343. ;Delete char at cursor
  1344.   mov dx, di                    ;Save cursor column
  1345.   cmp [BYTE es:di], ' '         ;If deleting a space at end of line,
  1346.   jne Delete0
  1347.   call RScanE
  1348.   mov di, dx                    ; join line below
  1349.   je Join
  1350. Delete0:
  1351.   mov [changed?], -1
  1352.   push di                       ; else slide text left
  1353.   push cx
  1354.   push ds
  1355.   mov cx, LINEWIDTH - 1
  1356.   sub cx, di
  1357.   mov si, di
  1358.   inc si
  1359.   mov ax, es
  1360.   mov ds, ax
  1361.   rep movsb
  1362.   mov [BYTE di], ' '            ;Blank last character on line
  1363.   pop ds
  1364.   pop cx
  1365.   pop di
  1366. Ret1:
  1367.   ret
  1368.  
  1369. UndeleteLine:
  1370.   mov bp, [blockPtrsLast]       ;Abort if no lines are in buffer
  1371.   cmp bp, OFFSET blockPtrs
  1372.   ja @@L0
  1373.   jmp Beep
  1374. @@L0:
  1375.   dec bp                        ;Else move pointer to top line of delete buffer
  1376.   dec bp
  1377.   mov [blockPtrsLast], bp
  1378.   cmp di, [lMargin]             ;If cursor is at or before L margin,
  1379.   ja @@L1
  1380.   mov cx, 1
  1381.   call OpenRow                  ;Start new row below current one
  1382.   mov [bx+2], es                ;Swap rows to insert undeleted above current
  1383.   mov ax, [bp]                  ;Retrieve and store pointer to undeleted line
  1384.   mov [bx], ax
  1385.   jmp NewLine
  1386. @@L1:
  1387.   mov cx, LINEWIDTH             ;Cursor past start of line
  1388.   sub cx, di                    ;Copy popped line over current one
  1389.   push di
  1390.   push ds
  1391.   mov ds, [bp]
  1392.   sub si, si
  1393.   rep movsb
  1394.   pop ds
  1395.   pop di
  1396.   ret
  1397.  
  1398. Join:
  1399. ;Join lower line to current line at cursor
  1400.   cmp bx, [lastLine]            ;Abort if this is the last line of the file
  1401.   je @@Lx
  1402.   push bx                       ;Save registers
  1403.   push di
  1404.   push ds
  1405.   push di
  1406.   push es
  1407.   mov es, [bx + 2]              ;Get next line's segment
  1408.   push es                       ;Save a copy
  1409.   mov dx, di
  1410.   sub di, di                    ;Remove leading spaces
  1411.   call RScanE
  1412.   dec di
  1413.   mov [temp], di
  1414.   call EndLine                  ;Find first non-space char from end
  1415.   add dx, di                    ;If concatenated line is too long, abort.
  1416.   sub dx, [temp]
  1417.   cmp dx, LINEWIDTH
  1418.   jbe @@L0
  1419.   call Beep
  1420.   pop ax
  1421.   pop es
  1422.   pop di
  1423.   pop ds
  1424.   pop ax
  1425.   pop ax
  1426.   jmp Ret1
  1427. @@L0:
  1428.   mov cx, di                    ;Count = lower line length minus leading spaces
  1429.   sub cx, [temp]
  1430.   mov si, [temp]                ;Source = start of text on lower line
  1431.   pop ds
  1432.   pop es                        ;Destination = present cursor location
  1433.   pop di
  1434.   rep movsb                     ;Concatenate lines
  1435.   pop ds
  1436.   inc bx                        ;Delete lower line
  1437.   inc bx
  1438.   call DeleteLineNS
  1439.   pop di                        ;Restore pointers and return
  1440.   pop bx
  1441. @@Lx:
  1442.   jmp NewLineC
  1443.  
  1444. Insert:
  1445. ;Insert or overwrite at cursor
  1446.   mov [justFound?], 0
  1447.   test [inserting?], -1         ;If inserting, open up space for new character
  1448.   jz Insert1
  1449.   mov si, [RMargin]             ;If line is full, split it
  1450.   cmp [BYTE es:si], ' '
  1451.   je Insert0
  1452.   push ax
  1453.   push bx
  1454.   push di
  1455.   call CRet
  1456.   pop di
  1457.   pop bx
  1458.   call NewLine
  1459.   pop ax
  1460.   jmp SHORT Insert1
  1461. Insert0:
  1462.   push ax
  1463.   push cx
  1464.   push ds
  1465.   mov ax, es
  1466.   mov ds, ax
  1467.   mov si, LINEWIDTH - 1
  1468.   mov cx, si
  1469.   sub cx, di
  1470.   mov di, si
  1471.   dec si
  1472.   std
  1473.   rep movsb
  1474.   cld
  1475.   pop ds
  1476.   pop cx
  1477.   pop ax
  1478. Insert1:
  1479.   stosb                         ;Add character
  1480.   mov [changed?], -1
  1481.   cmp di, [rMargin]             ;Wrap if at R margin
  1482.   ja WrapLine
  1483.   ret
  1484.  
  1485. WrapLine:
  1486. ;Wrap last word on current line
  1487.   cmp [BYTE es:di-1], ' '
  1488.   je @@L1
  1489.   call WordLeft
  1490. @@L1:
  1491.   call CRet
  1492.   mov es, [bx]
  1493.   cmp [BYTE es:di], ' '
  1494.   jne @@L2
  1495.   jmp DeleteWordR
  1496. @@L2:
  1497.   jmp EndLine
  1498.  
  1499. Wrap:
  1500. ;Wrap paragraph starting at present line
  1501.   mov di, [rMargin]             ;Start at R margin of present line
  1502.   cmp bx, [lastLine]            ;Quit if we're at end of file
  1503.   je @@Lx
  1504. @@L1:
  1505.   mov dx, di
  1506.   call RScanE                   ;Is there text between here and end of line?
  1507.   cmp di, LINEWIDTH
  1508.   mov di, dx
  1509.   je @@L2
  1510.   call LScanNE                  ;Yes.  Is there a space at which to split line?
  1511.   or di, di
  1512.   mov di, dx
  1513.   jle @@Lx                      ;No, stop wrapping
  1514.   call FindBreak                ;Yes, split this line, drop to lower line
  1515.   call CRet
  1516.   jmp Wrap
  1517. @@L2:
  1518.   call EndLine                  ;Put cursor after end of text on line.
  1519.   mov al, [es:di-1]             ;If last char is in {.;:!?}, add two spaces
  1520.   cmp al, 40h
  1521.   jae @@L2b
  1522.   cmp al, '.'
  1523.   je @@L2a
  1524.   cmp al, ';'
  1525.   je @@L2a
  1526.   cmp al, ':'
  1527.   je @@L2a
  1528.   cmp al, '!'
  1529.   je @@L2a
  1530.   cmp al, '?'
  1531.   jne @@L2b
  1532. @@L2a:
  1533.   inc di
  1534. @@L2b:
  1535.   inc di                        ;After other chars, just add one space
  1536.   mov dx, [rMargin]             ;Calculate spaces remaining on upper line ->DX
  1537.   sub dx, di
  1538.   ja @@L3                       ;If no room for more text, next line
  1539.   call Down
  1540.   call KeepWrapping?
  1541.   jnc @@Lx
  1542.   jmp Wrap
  1543. @@L3:
  1544.   mov [topSegPtr], bx           ;Save pointers for this line
  1545.   mov [topSeg], es
  1546.   mov [topIndex], di
  1547.   call Down                     ;Drop down to next line
  1548.   call KeepWrapping?            ;Does text start at L margin?
  1549.   jc @@L4
  1550. @@Lx:
  1551.   jmp NewLine                   ;No, we're done
  1552. @@L4:
  1553.   add di, dx                    ;Yes, move words up from lower line
  1554.   call FindBreak                ;Find right place to break line
  1555.   mov dx, di                    ;Set up pointers and count for move
  1556.   mov si, [lMargin]
  1557.   sub bp, si
  1558.   jnz @@L4a                     ;If nothing will fit, next line
  1559.   jmp Wrap
  1560. @@L4a:
  1561.   mov ax, es
  1562.   mov di, [topIndex]
  1563.   mov es, [topSeg]
  1564.   mov bx, [topSegPtr]
  1565.   mov ds, ax
  1566.   mov cx, bp
  1567.   rep movsb                     ;Move part of lower line up
  1568.   mov cx, cs
  1569.   mov ds, cx
  1570.   cmp dx, LINEWIDTH - 1         ;If nothing left on lower line,
  1571.   jne @@L5
  1572.   call Down
  1573.   call DeleteLine               ;Delete it
  1574.   cmp bx, [lastLine]            ;Done if that was last line
  1575.   je @@Lx
  1576.   mov es, [topSeg]              ;Get top line back and keep wrapping
  1577.   mov bx, [topSegPtr]
  1578.   jmp Wrap
  1579. @@L5:
  1580.   call Down                     ;Else slide lower line left
  1581.   mov si, dx
  1582.   mov di, [lMargin]
  1583.   call CloseGap
  1584.   jmp Wrap                      ;Continue wrapping with lower line
  1585.  
  1586. KeepWrapping?:
  1587. ;Set DI to L margin.  Return C = 1 if line <- BX has text starting at L margin.
  1588.   mov di, [lMargin]
  1589.   cmp [BYTE es:di], ' '         ;Fail if space at margin
  1590.   je @@Lx
  1591.   or di, di
  1592.   je @@L1
  1593.   push di
  1594.   dec di                        ; or chars before margin
  1595.   call LScanE
  1596.   pop di
  1597.   jne @@Lx
  1598. @@L1:
  1599.   stc                           ; else return C = 1
  1600.   db 3Ch                        ;'cmp al, ?' trick to skip one byte
  1601. @@Lx:
  1602.   clc
  1603.   ret
  1604.  
  1605. FindBreak:
  1606. ;Find place to break line while wrapping.  Return BP = last char to move.
  1607.   cmp [BYTE es:di], ' '
  1608.   je @@L1
  1609.   inc di
  1610.   call WordLeft
  1611.   mov bp, di
  1612.   ret
  1613. @@L1:
  1614.   mov bp, di
  1615.   jmp WordRight
  1616.  
  1617. DeleteToEOL:
  1618. ;Delete from cursor to end of line
  1619.   mov [justFound?], 0
  1620.   push bx                       ;Save regs to return to current cursor position
  1621.   push di
  1622.   push es
  1623.   push [word autoIndent?]       ;Turn autoIndent off
  1624.   mov [autoIndent?], 0
  1625.   call Cret                     ;Do Enter then delete lower line
  1626.   call DeleteWordL
  1627.   call DeleteLine
  1628.   pop [word autoIndent?]
  1629.   pop es
  1630.   pop di
  1631.   pop bx
  1632.   jmp NewLine
  1633.  
  1634. DeleteLine:
  1635. ;Delete cursor line and append to buffer
  1636.   mov [changed?], -1
  1637.   cmp bx, [lastLine]            ;If last line of file, just blank it
  1638.   jne @@L1
  1639.   jmp BlankThisLine
  1640. @@L1:
  1641.   mov bp, [blockPtrsLast]       ;Save segment of current line in delete buffer
  1642.   mov [bp], es
  1643.   inc bp
  1644.   inc bp
  1645.   mov [blockPtrsLast], bp
  1646. DeleteLineNS:                   ;Enter here if we don't want to save line
  1647.   mov di, bx                    ;Delete line:  destination = this line
  1648.   mov si, di                    ;Source = next line
  1649.   inc si
  1650.   inc si
  1651.   mov cx, [lastLine]            ;Count = number of lines from here to end
  1652.   mov ax, cx
  1653.   dec ax
  1654.   dec ax
  1655.   mov [lastLine], ax
  1656.   sub cx, bx
  1657.   shr cx, 1
  1658.   mov ax, ds                    ;Move line segment values above cursor down
  1659.   mov es, ax
  1660.   rep movsw
  1661.   mov di, [lMargin]             ;Home cursor on new line
  1662.   jmp NewLine
  1663.  
  1664. DeleteWordL:
  1665. ;Delete left to space or column zero
  1666.   mov ah, 2                     ;Check for Ctrl key down.  ^[ or ESC?
  1667.   int 16h                       ;If ^[, delete word left (ignore ESC)
  1668.   and al, 4
  1669.   je DWLx
  1670.   mov si, di                    ;Save cursor column
  1671.   call WordLeft                 ;Tab left one word
  1672. CloseGap:
  1673.   push di                       ;Close gap between di and si cursor positions
  1674.   mov cx, LINEWIDTH
  1675.   sub cx, si
  1676.   push ds
  1677.   mov ax, es
  1678.   mov ds, ax
  1679.   rep movsb
  1680.   mov cx, LINEWIDTH             ;Pad end of line with spaces
  1681.   sub cx, di
  1682.   mov al, ' '
  1683.   rep stosb
  1684.   pop ds
  1685.   pop di
  1686.   mov [changed?], -1
  1687. DWLx:
  1688.   ret
  1689.  
  1690. DeleteWordR:
  1691. ;Delete right to space or end of line
  1692.   mov si, di                    ;Save cursor
  1693.   push di
  1694.   call WordRight                ;Tab right one word
  1695.   xchg si, di                   ;Close up space between si and di
  1696.   pop di
  1697.   jmp CloseGap
  1698.  
  1699. ToggleIns:
  1700.   not [inserting?]
  1701.   ret
  1702.  
  1703. Jump:
  1704. ;Jump to line number n
  1705.   mov si, OFFSET gotoMsg
  1706.   call Prompt
  1707.   call GetInt
  1708.   dec ax
  1709.   shl ax, 1
  1710.   mov bx, ax
  1711.   add bx, OFFSET linePtrs
  1712.   mov [justFound?], 0
  1713.   jmp SHORT JL1                 ;Jump to address
  1714.  
  1715. GetInt:
  1716. ;Get a decimal integer from keyboard to AX.  Carry set on improper input.
  1717. ;Abort if null input.
  1718.   push bx
  1719.   push cx
  1720.   push dx
  1721.   push si
  1722.   mov dx, OFFSET buffer
  1723.   call GetString                ;Input a string
  1724.   mov cx, ax                    ;Construct integer a digit at a time
  1725.   mov si, OFFSET buffer
  1726.   sub ax, ax
  1727.   mov bh, 10
  1728. @@L1:
  1729.   mov bl, [si]                  ;Get next char
  1730.   inc si
  1731.   sub bl, '0'
  1732.   jc @@Lx                       ;Exit with carry set if not a digit
  1733.   cmp bl, '9'
  1734.   cmc
  1735.   jc @@Lx
  1736.   mul bh                        ;AX := AX + (new digit - '0')
  1737.   add al, bl
  1738.   adc ah, 0
  1739.   jc @@Lx                       ;Check for overflow
  1740.   loop @@L1                     ;Next char
  1741. @@Lx:
  1742.   pop si                        ;Return with int in AX, carry set if error
  1743.   pop dx
  1744.   pop cx
  1745.   pop bx
  1746.   ret
  1747. @@La:
  1748.   mov si, OFFSET cancelledMsg
  1749.   jmp Abort
  1750.  
  1751. SetLabel:
  1752. ;Set label 0-9 at current line
  1753.   call GetLabel
  1754.   mov [si], bx
  1755.   ret
  1756.  
  1757. GotoLabel:
  1758. ;Goto label 0-9 previously set by SetLabel
  1759.   call GetLabel
  1760.   cmp [WORD si], 0              ;Cancel if label not assigned
  1761.   je JLx
  1762.   mov bx, [si]                  ;Retrieve address
  1763.   mov [justFound?], 0
  1764. JL1:
  1765.   mov ax, bx
  1766.   sub ax, 8                     ;Make cursor line fifth from top
  1767.   cmp ax, OFFSET linePtrs
  1768.   jge @@L1
  1769.   mov ax, OFFSET linePtrs
  1770. @@L1:
  1771.   mov [top], ax
  1772. JLx:
  1773.   jmp NewLine0
  1774.  
  1775. GetLabel:
  1776.   mov si, OFFSET setLabelMsg
  1777.   call Prompt
  1778.   mov ah, 8                     ;Get char from keyboard
  1779.   int 21h
  1780.   mov dl, al                    ;Save copy to echo
  1781.   sub al, '0'                   ;Don't accept input if not a digit
  1782.   jl GetLabel
  1783.   cmp al, 9
  1784.   jg GetLabel
  1785.   mov ah, 2
  1786.   mov cl, al
  1787.   int 21h
  1788.   mov si, OFFSET LabelTable     ;Form index into LabelTable
  1789.   shl cl, 1
  1790.   sub ch, ch
  1791.   add si, cx                    ;Return address of label storage in SI
  1792.   ret
  1793.  
  1794. SetTabs:
  1795. ;Set tab width
  1796.   mov si, OFFSET setTabsMsg
  1797.   call Prompt
  1798.   call GetInt
  1799.   mov [tabSize], al
  1800.   ret
  1801.  
  1802. AutoIndent:
  1803. ;Toggle autoindent mode
  1804.   not [autoIndent?]
  1805.   ret
  1806.  
  1807. Kill:
  1808. ;Toggle changed? flag for file changes to be discarded on exit
  1809.   not [changed?]
  1810.   ret
  1811.  
  1812. Save:
  1813. ;Write lines to file, renaming old version with .BAK extension
  1814.   cmp [changed?], 0             ;If no changes, done.
  1815.   je XSave
  1816.   push dx
  1817.   push di
  1818.   push es
  1819.   push bx
  1820.   mov al, [newFile?]            ;If a new file, create it first
  1821.   or al, [isBAKed?]             ;If already BAKed up, no .BAK needed
  1822.   jnz DoSave
  1823.   mov ax, ds                    ;Else make new ASCIIZ str with .BAK extension
  1824.   mov es, ax
  1825.   mov si, OFFSET fName
  1826.   mov di, OFFSET fNameBAK
  1827. @@L1:
  1828.   lodsb
  1829.   cmp al, '.'
  1830.   je @@L2
  1831.   or al, al
  1832.   je @@L2
  1833.   stosb
  1834.   jmp SHORT @@L1
  1835. @@L2:
  1836.   mov cx, 5
  1837.   mov si, OFFSET BAK
  1838.   rep movsb
  1839.   mov ah, 41h                   ;Delete old back-up copy if present
  1840.   mov dx, OFFSET fNameBAK
  1841.   int 21h
  1842.   mov dx, OFFSET fName          ;Rename current file to file.BAK
  1843.   mov di, OFFSET fNameBAK
  1844.   mov ah, 56h
  1845.   int 21h
  1846. DoSave:
  1847.   mov ah, 3Ch                   ;CREATe new file with old name
  1848.   sub cx, cx
  1849.   mov dx, OFFSET fName
  1850.   int 21h
  1851.   jc CantOpen
  1852.   mov [fHandle], ax
  1853.   mov [isBAKed?], -1            ;Set flag so we only make .BAK file once
  1854.   mov bx, OFFSET linePtrs       ;Write file
  1855.   call WriteFile
  1856.   mov ah, 3Eh                   ;Close file
  1857.   mov bx, [fHandle]
  1858.   int 21h
  1859.   pop bx
  1860.   pop es
  1861.   pop di
  1862.   pop dx
  1863.   mov [changed?], 0
  1864. XSave:
  1865.   ret
  1866.  
  1867. WriteFile:
  1868. ;Write lines out to file [fHandle] starting at BX and ending at [lastLine]
  1869.   push es
  1870.   push di
  1871.   mov di, OFFSET buffer
  1872. @@L1:
  1873.   mov si, di                    ;Preserve file buffer pointer
  1874.   mov es, [bx]                  ;Strip trailing blanks
  1875.   mov cx, LINEWIDTH
  1876.   mov di, LINEWIDTH - 1
  1877.   mov al, ' '
  1878.   std
  1879.   repe scasb
  1880.   cld
  1881.   je @@L1a
  1882.   inc cx
  1883. @@L1a:
  1884.   mov ax, ds                    ;Copy line to file buffer
  1885.   mov dx, es
  1886.   mov es, ax
  1887.   mov ds, dx
  1888.   mov di, si
  1889.   sub si, si
  1890.   rep movsb
  1891.   mov ax, CRLF                  ;Stick a CRLF on the end
  1892.   stosw
  1893.   mov ax, ds
  1894.   mov dx, es
  1895.   mov es, ax
  1896.   mov ds, dx
  1897.   cmp di, OFFSET Buffer + BUFFERLENGTH - 80  ;If buffer is almost full,
  1898.   jl @@L2
  1899.   call WriteBuffer              ; write it
  1900. @@L2:
  1901.   inc bx                        ;Next line, loop until all lines are written
  1902.   inc bx
  1903.   cmp bx, [lastLine]
  1904.   jle @@L1
  1905.   dec di                        ;Delete last CR, LF
  1906.   dec di
  1907.   call WriteBuffer              ;Write final partial buffer to file and exit
  1908.   pop di
  1909.   pop es
  1910.   ret
  1911.  
  1912. FileError:
  1913.   mov si, OFFSET fileErrorMsg
  1914.   jmp Abort
  1915. CantOpen:
  1916.   mov si, OFFSET cantOpenMsg
  1917.   jmp Abort
  1918. NoRoom:
  1919.   mov si, OFFSET noRoomMsg
  1920.   jmp Abort
  1921. ReadError:
  1922.   mov si, OFFSET rdErrorMsg
  1923.   jmp Abort
  1924. DiskFull:
  1925.   mov dx, OFFSET fName          ;Attempt to delete file first
  1926.   mov ah, 41h
  1927.   int 21h
  1928.   mov si, OFFSET diskFullMsg
  1929.   jmp Abort
  1930.  
  1931. WriteBuffer:
  1932. ;Write text in buffer to disk
  1933.   push bx
  1934.   mov bx, [fHandle]
  1935.   mov cx, di
  1936.   mov dx, OFFSET Buffer
  1937.   sub cx, dx
  1938.   jle @@L1
  1939.   push cx
  1940.   mov ah, 40h
  1941.   int 21h
  1942.   jc FileError
  1943.   pop cx
  1944.   cmp cx, ax
  1945.   jne DiskFull
  1946.   mov di, OFFSET Buffer
  1947. @@L1:
  1948.   pop bx
  1949.   ret
  1950.  
  1951. Exit:
  1952.   call Save                     ;Save file if changed
  1953.   call RestoreCursor
  1954.   mov ax, 4C00h                 ;Bye!
  1955.   int 21h
  1956.  
  1957. RestoreCursor:
  1958.   mov cx, [cursorShape]         ;Restore standard cursor size
  1959.   mov ah, 1
  1960.   int 10h
  1961.   mov dx, 1800h                 ;Put cursor at bottom of screen
  1962.   sub bh, bh
  1963.   mov ah, 2
  1964.   int 10h
  1965.   ret
  1966.  
  1967. BeginBlock:
  1968. ;Start marking block for block operation
  1969.   mov [marking?], -1
  1970.   mov [mark], bx
  1971.   ret
  1972.  
  1973. Unmark:
  1974. ;Clear marking
  1975.   mov [marking?], 0
  1976.   ret
  1977.  
  1978. InsertBlock:
  1979. ;Insert from buffer or named file
  1980.   push bx
  1981.   call FileOrBuffer?            ;From file or buffer?
  1982.   je InsertBuffer
  1983.   mov dx, OFFSET fNameTemp      ;If file, open it
  1984.   mov ax, 3D00h
  1985.   int 21h
  1986.   jnc @@L1
  1987.   jmp CantOpen
  1988. @@L1:
  1989.   mov dx, ax                    ;Load file
  1990.   mov bx, OFFSET blockPtrs
  1991.   mov di, bx
  1992.   call ReadFile
  1993.   mov cx, bx
  1994.   mov bx, dx                    ;Close file
  1995.   mov ah, 3Eh
  1996.   int 21h
  1997.   jmp SHORT DoInsert
  1998. InsertBuffer:                   ;Insert from buffer
  1999.   mov bp, [blockPtrsLast]       ;Abort if empty
  2000.   cmp bp, OFFSET blockPtrs
  2001.   jne @@L0
  2002.   pop bx
  2003.   jmp na
  2004. @@L0:
  2005.   test [needCopies?], -1        ;If not just moving lines, need to duplicate
  2006.   jz @@L2
  2007.   push bx
  2008.   push es
  2009.   push ds
  2010.   mov bx, bp
  2011.   mov dx, [heapPtr]
  2012. @@L1:
  2013.   dec bx                        ;Copy contents of buffered lines to new ones
  2014.   dec bx
  2015.   mov ds, [cs:bx]
  2016.   sub si, si
  2017.   mov es, dx
  2018.   sub di, di
  2019.   mov [cs:bx], es
  2020.   mov cx, LINEWIDTH
  2021.   rep movsb
  2022.   add dx, 5
  2023.   cmp dx, 0A000h
  2024.   jb @@L1a
  2025.   jmp NoRoom
  2026. @@L1a:
  2027.   cmp bx, OFFSET blockPtrs
  2028.   ja @@L1
  2029.   pop ds
  2030.   pop es
  2031.   pop bx
  2032.   mov [heapPtr], dx
  2033. @@L2:
  2034.   mov [needCopies?], -1
  2035.   mov cx, bp
  2036. DoInsert:
  2037.   pop bx
  2038.   sub cx, OFFSET blockPtrs      ;Get count of lines to move
  2039.   shr cx, 1
  2040.   call OpenRow                  ;Open that much room in array of seg pointers
  2041.   mov si, OFFSET blockPtrs      ;Copy new lines into opening
  2042.   mov di, bx
  2043.   mov ax, ds
  2044.   mov es, ax
  2045.   rep movsw
  2046.   sub di, di
  2047.   jmp NewLineC
  2048.  
  2049. FileOrBuffer?:
  2050. ;Prompt for file name if Shift is down, put it in fNameTemp.
  2051. ;If return with Z set, Shift was up, indicating buffer is to be used.
  2052.   call Shifted?                 ;If shift is down, prompt for file name
  2053.   jz Ret4
  2054.   mov dx, OFFSET fNameTemp
  2055.   call GetFileName
  2056.   jnz Ret4                      ;If null string returned, abort
  2057.   mov si, OFFSET cancelledMsg
  2058.   jmp Abort
  2059.  
  2060. Shifted?:
  2061. ;Get shift key status, returned in AL.  Z set if no shift.
  2062.   mov ah, 2
  2063.   int 16h
  2064.   and al, 3
  2065. Ret4:
  2066.   ret
  2067.  
  2068. EmptyBuffer:
  2069. ;If Shifted, write block buffer to file, else discard
  2070.   mov bp, [blockPtrsLast]       ;Abort if buffer is empty
  2071.   cmp bp, OFFSET blockPtrs
  2072.   jne @@L0
  2073.   jmp Beep
  2074. @@L0:
  2075.   call Shifted?                 ;If shifted, write to file
  2076.   je @@L1
  2077.   push bx
  2078.   mov dx, OFFSET fNameTemp
  2079.   call GetFileName
  2080.   mov si, OFFSET blockPtrs
  2081.   mov bx, bp
  2082.   dec bx
  2083.   dec bx
  2084.   call WriteBlock
  2085.   pop bx
  2086. @@L1:
  2087.   mov ax, OFFSET blockPtrs      ;Else just reset buffer pointer
  2088.   mov [blockPtrsLast], ax
  2089. @@Lx:
  2090.   ret
  2091.  
  2092. WriteBlock:
  2093. ;Write block to file.  SI -> starting seg pointer, BX -> ending seg pointer.
  2094.   push [lastLine]               ;Copy block buffered lines to file:
  2095.   push [fHandle]
  2096.   mov ah, 3Ch                   ;CREATe file
  2097.   sub cx, cx
  2098.   mov dx, OFFSET fNameTemp
  2099.   int 21h
  2100.   jnc @@L1
  2101.   jmp CantOpen
  2102. @@L1:
  2103.   mov [fHandle], ax             ;Write it
  2104.   mov [lastLine], bx
  2105.   mov bx, si
  2106.   call WriteFile
  2107.   mov bx, [fHandle]             ;Close it
  2108.   mov ah, 3Eh
  2109.   int 21h
  2110.   pop [fHandle]
  2111.   pop [lastLine]
  2112.   ret
  2113.  
  2114. Copy:
  2115. ;Copy marked lines, to file if shift is down, otherwise to buffer.
  2116.   test [marking?], -1          ;Abort with a beep if not already marking,
  2117.   jnz @@L1
  2118.   mov si, OFFSET notMarkingMsg
  2119.   jmp Abort
  2120. @@L1:
  2121.   mov si, [mark]
  2122.   cmp bx, si                    ;If mark comes after here, exchange
  2123.   jae @@L2
  2124.   xchg bx, si
  2125.   mov [mark], si                ; save in this order for possible delete
  2126. @@L2:
  2127.   push bx
  2128.   push di
  2129.   push es
  2130.   call FileOrBuffer?            ;If Shift key was down when command entered,
  2131.   je @@L4
  2132.   call WriteBlock
  2133.   mov di, OFFSET blockPtrs
  2134.   jmp SHORT @@Lx
  2135. @@L4:
  2136.   mov cx, bx                    ;If no Shift, move marked lines to buffer
  2137.   sub cx, si
  2138.   shr cx, 1
  2139.   inc cx
  2140.   mov di, OFFSET blockPtrs
  2141.   mov ax, ds
  2142.   mov es, ax
  2143.   rep movsw
  2144. @@Lx:
  2145.   mov [blockPtrsLast], di       ;Save pointer to last line
  2146.   pop es
  2147.   pop di
  2148.   pop bx
  2149.   mov [marking?], 0
  2150.   ret
  2151.  
  2152. DeleteBlock:
  2153. ;Do a Copy, then delete copied lines
  2154.   call Copy                     ;Copy block to file or buffer
  2155.   mov si, bx                    ;Close up copied lines:
  2156.   inc si                        ;SI = cursor line + 2
  2157.   inc si
  2158.   mov di, [mark]                ;DI = start of marking
  2159.   mov cx, [lastLine]            ;CX = number of lines from here to end
  2160.   sub cx, bx
  2161.   push es
  2162.   mov ax, ds
  2163.   mov es, ax
  2164.   rep movsb
  2165.   pop es
  2166.   dec di
  2167.   dec di
  2168.   cmp di, OFFSET linePtrs       ;If whole file deleted, start new file
  2169.   jae @@L1
  2170.   call NewFile
  2171.   jmp NewLineC
  2172. @@L1:
  2173.   mov [lastLine], di            ;Else store index of new last line
  2174.   mov di, [lMargin]             ;Point cursor to start of old marked line
  2175.   mov bx, [mark]
  2176.   mov [needCopies?], 0
  2177.   jmp NewLineC
  2178.  
  2179. Find:
  2180. ;Prompt for and find next <string>.  If Shifted, reuse last <string>.
  2181.   push bx
  2182.   push es
  2183.   push di
  2184.   call Right
  2185.   test [autoReplace?], -1       ;If doing replace all, bypass shift check
  2186.   js @@L0
  2187.   jnz @@L1
  2188.   call Shifted?                 ;If Shifted,
  2189.   jnz @@L1
  2190. @@L0:
  2191.   mov si, OFFSET findMsg        ;Get search string
  2192.   mov dx, OFFSET findString
  2193.   call GetCountedString
  2194. @@L1:
  2195.   mov si, OFFSET findString
  2196.   lodsw
  2197.   dec al
  2198.   mov dl, al
  2199.   mov al, ah
  2200.   mov cx, LINEWIDTH
  2201.   sub cx, di
  2202. FindLoop:
  2203.   repne scasb                   ;Scan for first char of Find string
  2204.   jne FindNextLine
  2205.   push di                       ;Once found, compare rest of string
  2206.   mov dh, cl
  2207.   mov cl, dl
  2208.   mov si, OFFSET findString + 2
  2209.   repe cmpsb
  2210.   je Found
  2211.   pop di                        ;Match failed.  Scan again for 1st char.
  2212.   mov cl, dh
  2213.   jmp FindLoop
  2214. FindNextLine:
  2215.   inc bx                        ;Search next line (until EOF)
  2216.   inc bx
  2217.   mov es, [bx]
  2218.   sub di, di
  2219.   mov cl, LINEWIDTH
  2220.   cmp bx, [lastLine]
  2221.   jbe FindLoop
  2222.   mov si, OFFSET notFoundMsg    ;Not found
  2223.   test [autoReplace?], -1       ;If doing auto-replace, return
  2224.   jz @@L1
  2225.   mov [autoReplace?], 0
  2226.   pop di
  2227.   pop es
  2228.   pop bx
  2229.   ret
  2230. @@L1:
  2231.   call Print0                   ;Else restore cursor, abort with error message
  2232.   pop di
  2233.   pop es
  2234.   pop bx
  2235.   jmp na
  2236. Found:
  2237.   pop di
  2238.   dec di
  2239.   add sp, 6
  2240.   mov [justFound?], -1
  2241.   mov ax, bx                    ;Show found line 5 below top of screen
  2242.   jmp JL1
  2243.  
  2244. GetCountedString:
  2245. ;Print prompt string at [SI], read counted string into [DX].
  2246. ;Returns count (minus CR/LF) in AL.  DX is advanced one to start of string.
  2247.   push di
  2248.   push dx
  2249.   call Prompt                   ;Display prompt
  2250.   pop dx
  2251.   mov di, dx
  2252.   inc dx
  2253.   call GetString                ;Get input string
  2254.   mov [di], al                  ;Store count in front of string
  2255.   pop di
  2256.   ret
  2257.  
  2258. Replace:
  2259.   push di
  2260.   test [autoReplace?], -1       ;If using auto-replace command,
  2261.   jz @@L0
  2262.   jns @@L2                      ;Skip shift check
  2263.   mov [autoReplace?], 1
  2264.   jmp SHORT @@L1a               ;Get replace string if shifted and first pass
  2265. @@L0:
  2266.   test [justFound?], -1         ;Beep if not immediately preceded by a Find
  2267.   jnz @@L1
  2268. @@LE:
  2269.   pop di
  2270.   jmp na
  2271. @@L1:
  2272.   call Shifted?                 ;If not Shifted, prompt for replace string
  2273.   jnz @@L2
  2274. @@L1a:
  2275.   mov si, OFFSET replaceMsg
  2276.   mov dx, OFFSET replaceString
  2277.   call GetCountedString
  2278. @@L2:
  2279.   mov si, OFFSET replaceString
  2280.   lodsb
  2281.   sub ah, ah
  2282.   mov dx, si
  2283.   push ax
  2284.   push dx
  2285.   sub ch, ch                    ;Compare lengths of find and replace strings
  2286.   mov cl, al
  2287.   sub cl, [byte findString]     ;If replace string is longer,
  2288.   je @@L6
  2289.   jb @@L4
  2290.   xchg dx, di
  2291.   call EndLine                  ; make sure there will be enough room on line
  2292.   xchg dx, di
  2293.   add dl, cl
  2294.   cmp dl, LINEWIDTH
  2295.   ja @@LE
  2296. @@L3:
  2297.   call Insert0                  ; then insert extra characters
  2298.   loop @@L3
  2299.   jmp SHORT @@L6
  2300. @@L4:
  2301.   neg cl                        ;If shorter, delete difference
  2302. @@L5:
  2303.   call Delete0
  2304.   loop @@L5
  2305. @@L6:
  2306.   pop si                        ;Now copy new string over old
  2307.   pop cx
  2308.   sub ch, ch
  2309.   pop di
  2310.   rep movsb
  2311.   jmp NewLineC
  2312.  
  2313. ReplaceAll:
  2314. ;Find and replace all.  If shift not down, prompt for strings.
  2315.   push [top]
  2316.   push es
  2317.   push di
  2318.   push bx
  2319.   mov [autoReplace?], 1         ;Set flag: 1 = no shift, -1 = shift
  2320.   call Shifted?
  2321.   jne @@L1
  2322.   mov [autoReplace?], -1
  2323. @@L1:
  2324.   call Find                     ;Find/replace until flag reset by not found
  2325.   test [autoReplace?], -1
  2326.   jz @@Lx
  2327.   call Replace
  2328.   jmp @@L1
  2329. @@Lx:
  2330.   pop bx                        ;Restore original position
  2331.   pop di
  2332.   pop es
  2333.   pop [top]
  2334.   push si
  2335.   call ReDraw                   ;Refresh screen with any changes
  2336.   pop si
  2337.   push di
  2338.   call Print0                   ;Show notFoundMsg and exit
  2339.   pop di
  2340.   jmp NextNoRedraw
  2341.  
  2342. Help:
  2343.   push es
  2344.   push di
  2345.   mov es, [videoSegment]
  2346.   mov di, 160
  2347.   mov si, OFFSET helpMsg
  2348.   mov cx, 80 * 24
  2349.   mov ah, [attribNl]
  2350. @@L1:
  2351.   lodsb
  2352.   stosw
  2353.   loop @@L1
  2354.   mov si, OFFSET anyKeyMsg
  2355.   call Print0
  2356.   sub ah, ah
  2357.   int 16h
  2358.   pop di
  2359.   pop es
  2360.   ret
  2361.  
  2362. F3BAT:
  2363.   mov dl, '3'
  2364.   jmp SHORT FnBAT
  2365.  
  2366. F4BAT:
  2367.   mov dl, '4'
  2368.   jmp SHORT FnBAT
  2369.  
  2370. F5BAT:
  2371.   mov dl, '5'
  2372.   jmp SHORT FnBAT
  2373.  
  2374. F6BAT:
  2375.   mov dl, '6'
  2376.  
  2377. FnBAT:
  2378. ;Execute 'EFn.BAT f'  where n = DL and f = current file name minus extension
  2379.   push es
  2380.   push di
  2381.   mov [EXECFNumber], dl         ;Which .BAT file?
  2382.   mov ax, cs                    ;Copy file name to command line string
  2383.   mov es, ax
  2384.   mov di, OFFSET EXECFileName
  2385.   mov si, OFFSET fName
  2386.   sub cl, cl
  2387. @@L1:
  2388.   lodsb
  2389.   or al, al                     ; up to '.' or null (ie, omit extension)
  2390.   je @@L2
  2391.   cmp al, '.'
  2392.   je @@L2
  2393.   stosb
  2394.   inc cl
  2395.   jmp @@L1
  2396. @@L2:
  2397.   mov al, CR
  2398.   stosb
  2399.   add cl, 11
  2400.   mov [EXECBAT], cl             ;Store count to include file name
  2401.   pop di
  2402.   pop es
  2403.   mov ax, OFFSET EXECBAT
  2404.   jmp SHORT Shell1
  2405.  
  2406. Shell:
  2407. ;Shell to DOS
  2408.   mov ax, OFFSET EXECParams     ;Point to null command line tail
  2409. Shell1:
  2410.   mov [EXECCmdLineOff], ax
  2411.   push bx                       ;Save regs
  2412.   push es
  2413.   push di
  2414.   call Shifted?                 ;Get and save shift status
  2415.   pushf
  2416.   call Save                     ;Save file being edited
  2417.   popf                          ;If Shift was down, release file, buffers
  2418.   jz @@L1
  2419.   mov bx, ((PROGLENGTH + ESSENTIALS) SHR 4) + 1
  2420.   mov [swapped?], -1
  2421.   jmp SHORT @@L2
  2422. @@L1:
  2423.   mov bx, [heapPtr]             ; else keep file and buffers in memory
  2424.   mov ax, cs
  2425.   sub bx, ax
  2426.   mov [swapped?], 0
  2427. @@L2:
  2428.   mov ax, cs                    ;Free all unneeded memory
  2429.   mov es, ax
  2430.   mov ah, 4Ah
  2431.   int 21h
  2432.   jc SysErr
  2433.   call RestoreCursor            ;Restore cursor
  2434.   mov [SPTemp], sp              ;Do EXEC fn
  2435.   mov dx, [comspecPtrOff]
  2436.   mov ds, [comspecPtrSeg]
  2437.   mov bx, OFFSET EXECParams
  2438.   mov ax, 4B00h
  2439.   int 21h
  2440.   jc SysErr
  2441.   mov ax, cs                    ;Reclaim memory
  2442.   mov ds, ax
  2443.   mov ss, ax
  2444.   mov sp, [spTemp]
  2445.   mov ah, 4Ah
  2446.   mov bx, -1
  2447.   int 21h
  2448.   mov ah, 4Ah
  2449.   int 21h
  2450.   jc SysErr
  2451.   cmp [swapped?], 0             ;If edited file was swapped out,
  2452.   je XEXEC
  2453.   mov dx, OFFSET fName          ;Read it back in
  2454.   call OpenFile
  2455. XEXEC:
  2456.   pop di                        ;Restore regs and return
  2457.   pop es
  2458.   pop bx
  2459.   jmp NewLine
  2460.  
  2461. SysErr:
  2462.   mov si, OFFSET sysErrMsg
  2463.   jmp Abort
  2464.  
  2465.  
  2466. EndOfProgram:
  2467. END Orig